
#include "map.h"

map::map(bool useStack, bool lastRoom, bool debug){
    this->useStack = useStack;
    this->DEBUG = debug;
    (lastRoom)? this->exitDirection = 'T' : this->exitDirection = 'O';

    createMap();

    bool found  = false;
    put(visit(this->start,""));
    while(!found && !isEmpty()){
        tile t = get();

        //NSEW
        point n; n.x = t.location.x; n.y = t.location.y-1;
            if(isValidLocation(n) && !visited(n)){
                tile next = visit(n, t.path+"N");
                put(next);
                getExit(next, found);
            }
        point s; s.x = t.location.x; s.y = t.location.y+1;
            if(isValidLocation(s) && !visited(s)){
                tile next = visit(s, t.path+"S");
                put(next);
                getExit(next, found);
            }
        point e; e.x = t.location.x+1; e.y = t.location.y;
            if(isValidLocation(e) && !visited(e)){
                tile next = visit(e, t.path+"E");
                put(next);
                getExit(next, found);
            }
        point w; w.x = t.location.x-1; w.y = t.location.y;
            if(isValidLocation(w) && !visited(w)){
                tile next = visit(w, t.path+"W");
                put(next);
                getExit(next, found);
            }

    }
    if(!found){
        this->Solution="@";
    } else {
        this->Solution = this->Solution+"\n";
    }

}
string map::getSolution() {return this->Solution;}

void map::put(tile t){
    if(useStack)
        stack.push(t);
    else 
        queue.push(t);
}
tile map::get(){
    tile t;
    if(useStack){
        t = stack.top();
        stack.pop();
    }
    else{
        t = queue.front();
        queue.pop();
    }
    return t;
}
bool map::isEmpty(){
    if(useStack)
        return stack.empty();
    else
        return queue.empty();
}

bool map::isValidLocation(point p){
    if(p.x < 0 || p.x >= this->columns)
        return false;
    if(p.y < 0 || p.y >= this->rows)
        return false;
    if(data[p.y][p.x].c == '@')
        return false;
    return true;
}
tile map::visit(point p, string path){
    if(!isValidLocation(p)){
        invalidTileException e;
        throw e;
    }
    data[p.y][p.x].path = path;
    data[p.y][p.x].visited = true;
    return data[p.y][p.x];
}
bool map::visited(point p){
    return isValidLocation(p) && data[p.y][p.x].visited;
}
void map::getExit(tile t, bool &found){
    if(t.c=='O'||t.c=='T'){
        this->Solution = t.path;
        if(t.c=='O') this->Solution = this->Solution + this->exitDirection;
        found = true;
    }
}

void map::createMap(){

    string input = "";
    if(!getline(cin, input)){ cerr << "EoF!"; cerr.flush(); exit(1);}
    size_t index = input.find_first_of(" ");
        if(index == string::npos){
            cerr << "Error in reading rooms!\n";
            cerr.flush();
            exit(1);
        }
    this->rows = atoi((input.substr(0,index)).c_str());
    this->columns = atoi((input.substr(index+1)).c_str());
        if (this->rows <= 0 || this->columns <= 0){
            cerr << "Error in reading rooms - invalid rows or colums!\n";
            cerr.flush();
            exit(1);
        }

    data = NULL;
    data = new tile *[this->rows] ;
    for(unsigned i = 0 ; i < this->rows ; i++ )
        data[i] = new tile[this->columns];

    bool hasStart = false;
    bool hasEnd = false;

    for(unsigned r = 0 ; r < this->rows ; r++ ){
        if(!getline(cin, input)){ cerr << "EoF!"; cerr.flush(); exit(1);}
        if(input.size()<this->columns){
            cerr << "Error in reading rooms - invalid row!\n";
            cerr.flush();
            exit(1);
        }
        for(unsigned c = 0 ; c < this->columns ; c++ ){

            char t = input.at(c);
            if(t!='.'&&t!='@'&&t!='I'&&t!='O'&&t!='T'){
                cerr << "Error in reading rooms - invlaid char!\n";
                cerr.flush();
                exit(1);
            }
            if(t=='I'){
                if(hasStart){
                  cerr << "Error in reading rooms - too many starts!\n";
                  cerr.flush();
                  exit(1);
                }
                hasStart = true;
                point p; p.x = c; p.y = r;
                this->start = p;
            }
            if(t=='O'||t=='T'){
                if(hasEnd){
                  cerr << "Error in reading rooms - too many ends!\n";
                  cerr.flush();
                  exit(1);
                }
                hasEnd = true;
                if(t!=this->exitDirection){
                    cerr << "Error in reading rooms - invalid end!\n";
                    cerr.flush();
                    exit(1);
                }
                if(t=='O'){
                    //WESN
                    if(c==0){this->exitDirection='W';}
                    if(c==this->columns-1){this->exitDirection='E';}
                    if(r==this->rows-1){this->exitDirection='S';}
                    if(r==0){this->exitDirection='N';}
                }
            }

            point p; p.x = c; p.y = r;
            data[r][c].location = p;
            data[r][c].c = t;
            data[r][c].visited = false;
            data[r][c].path = "";
            
        }   
    }
    if(!hasStart || !hasEnd){
        cerr << "Error in reading rooms - missing start or end!\n";
        cerr.flush();
        exit(1);
    }


    printMap();
}
void map::printMap(){
    if(!this->DEBUG) return;
    for(unsigned r = 0 ; r < this->rows ; r++ ){
        for(unsigned c = 0 ; c < this->columns ; c++ ){
            cout<<data[r][c].c;
        }
        cout<<"\n";
    }
    cout.flush();
}

map::~map(){
    for(unsigned i = 0 ; i < this->rows ; i++ )
        delete[] data[i] ;
    delete[] data ;
    data = NULL;
}
map::map(const map &m){
    cannotCopyException e;
    throw e;
}
map &map::operator=(const map &m){
    cannotCopyException e;
    throw e;
    return *this;
}
